<html>
    <head>
        <title>CRUD应用服务</title>
    </head>
    <body>
        <script>
            /*
                        六、CRUD应用服务

                                    如果你需要创建一个简单的CRUD应用程序服务，它具有Create、Update、Delete和Get方法，你可以使用ABP的基类轻松构建你的服务。

                                    你可以从CrudAppService继承。


                                    1、CRUD 应用服务示例：
                                    
                                                创建从ICrudAppService接口继承的IBookAppService接口。

                                                    public interface IBookAppService : 
                                                        ICrudAppService< //  此接口声明了基本的CRUD操作
                                                            BookDto, // 输出Dto
                                                            Guid, // 主键类型
                                                            PagedAndSortedResultRequestDto, // 排序分页参数Dto
                                                            CreateUpdateBookDto, // 输入Dto
                                                            CreateUpdateBookDto> // 输入Dto
                                                    {
                                                    }

                                                ICrudAppService具有 "通用参数" 来获取实体的主键类型和CRUD操作的DTO类型
                                                    
                                                （它不获取实体类型，因为实体类型没有暴露给使用此接口的客户端）。

                                                ★、为应用程序服务创建接口是很好的做法，但ABP不需要。你可以跳过接口部分。、

                                                ICrudAppService声明以下方法：

                                                            public interface ICrudAppService<
                                                                TEntityDto,
                                                                in TKey,
                                                                in TGetListInput,
                                                                in TCreateInput,
                                                                in TUpdateInput>
                                                                : IApplicationService
                                                                where TEntityDto : IEntityDto<TKey>
                                                            {
                                                                Task<TEntityDto> GetAsync(TKey id);

                                                                Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input);

                                                                Task<TEntityDto> CreateAsync(TCreateInput input);

                                                                Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input);

                                                                Task DeleteAsync(TKey id);
                                                            } 
                                                        
                                                此示例中使用的DTO类是BookDto和CreateUpdateBookDto：
                                           
                                                            // 输出Dto
                                                            public class BookDto : AuditedEntityDto<Guid>
                                                            {
                                                                public string Name { get; set; }

                                                                public BookType Type { get; set; }

                                                                public float Price { get; set; }
                                                            }

                                                            // 输入Dto
                                                            public class CreateUpdateBookDto
                                                            {
                                                                [Required]
                                                                [StringLength(128)]
                                                                public string Name { get; set; }

                                                                [Required]
                                                                public BookType Type { get; set; } = BookType.Undefined;

                                                                [Required]
                                                                public float Price { get; set; }
                                                            }

                                                配置文档类：

                                                            public class MyProfile : Profile
                                                            {
                                                                public MyProfile()
                                                                {
                                                                    CreateMap<Book, BookDto>();
                                                                    CreateMap<CreateUpdateBookDto, Book>();
                                                                }
                                                            }


                                                        CreateUpdateBookDto由创建和更新操作共享，但你也可以使用单独的DTO类。

                                                最后，BookAppService的实现非常简单：

                                                            public class BookAppService : 
                                                                CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
                                                                                    CreateUpdateBookDto, CreateUpdateBookDto>,
                                                                IBookAppService
                                                            {
                                                                public BookAppService(IRepository<Book, Guid> repository) 
                                                                    : base(repository)
                                                                {
                                                                }
                                                            }

                                                CrudAppService实现ICrudAppService接口中声明的所有方法。然后，你可以添加自己的自定义方法或覆盖和自定义基方法。

                                                CrudAppService有不同的版本，得到不同数量的通用参数。使用适合你的。



                                2、AbstractKeyCrudAppService

                                                CrudAppService需要有一个Id属性作为实体的主键。如果你使用的是复合键，则不能使用它。

                                                AbstractKeyCrudAppService实现了相同的ICrudAppService接口，但这次没有假设你的实体为单字段主键。

                                             示例：

                                                    假设你有一个District实体，CityId和Name作为复合主键。

                                                    使用AbstractKeyCrudAppService需要自己实现 "DeleteByIdAsync" 和 "GetEntityByIdAsync" 方法：
                                                                
                                                                public class DistrictAppService
                                                                    : AbstractKeyCrudAppService<District, DistrictDto, DistrictKey>
                                                                {
                                                                    public DistrictAppService(IRepository<District> repository) 
                                                                        : base(repository)
                                                                    {
                                                                    }

                                                                    protected async override Task DeleteByIdAsync(DistrictKey id)
                                                                    {
                                                                        await Repository.DeleteAsync(d => d.CityId == id.CityId && d.Name == id.Name);
                                                                    }

                                                                    protected async override Task<District> GetEntityByIdAsync(DistrictKey id)
                                                                    {
                                                                        var queryable = await Repository.GetQueryableAsync();
                                                                        return await AsyncQueryableExecuter.FirstOrDefaultAsync(
                                                                            queryable.Where(d => d.CityId == id.CityId && d.Name == id.Name)
                                                                        );
                                                                    }
                                                                }

                                                    此实现要求你创建一个表示复合键的类：

                                                                public class DistrictKey
                                                                {
                                                                    public Guid CityId { get; set; }

                                                                    public string Name { get; set; }
                                                                }


                                 3、CRUD应用服务的授权  Authorize

                                            授权基础应用服务方法有两种方式：

                                                    (1)、你可以在服务的构造函数中设置策略属性（xxxPolcyName）。

                                                            eg： 
                                                                public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid>
                                                                {
                                                                    public MyPeopleAppService(IRepository<Person, Guid> repository) 
                                                                        : base(repository)
                                                                    {
                                                                        GetPolicyName = "...";  // 设置策略名称
                                                                        GetListPolicyName = "..."; // 设置策略名称
                                                                        CreatePolicyName = "..."; // 设置策略名称
                                                                        UpdatePolicyName = "..."; // 设置策略名称
                                                                        DeletePolicyName = "..."; // 设置策略名称
                                                                    }
                                                                }

                                                    (2)、你可以重写服务中的检查方法（CheckXxxPolcyAsync）。例子：

                                                            eg:
                                                                public class MyPeopleAppService : CrudAppService<Person, PersonDto, Guid>
                                                                {
                                                                    public MyPeopleAppService(IRepository<Person, Guid> repository) 
                                                                        : base(repository)
                                                                    {
                                                                    }

                                                                    // 重写CRUD服务基类中的检查方法
                                                                    protected async override Task CheckDeletePolicyAsync()
                                                                    {
                                                                        await AuthorizationService.CheckAsync("...");
                                                                    }
                                                                }


                                            你可以在CheckDeletePolicyAsync方法中执行任何逻辑。
                                        
                                            在任何未经授权的情况下，都应该抛出AbpAuthorizationException，如AuthorizationService.CheckAsync已经这样做了。



                                4、基本属性和方法 (CRUD 服务中的)

                                           CRUD应用程序服务基类提供了许多有用的基本方法，你可以覆盖以根据你的需求对其进行自定义。
                                           
                                           (1)、CRUD方法

                                                       这些是基本的CRUD方法。你可以覆盖其中任何一个以完全自定义操作。这里，方法的定义：
                                                       
                                                                Task<TGetOutputDto> GetAsync(TKey id);
                                                                Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input);
                                                                Task<TGetOutputDto> CreateAsync(TCreateInput input);
                                                                Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input);
                                                                Task DeleteAsync(TKey id);、


                                            (2)、查询方法

                                                      这些方法是可以控制如何从数据库中查询实体的底层方法。  

                                                            ①、CreateFilteredQuery以创建由给定输入过滤的IQueryable<TEntity>。

                                                                        如果你的TGetListInput类包含任何过滤器，则可以覆盖此方法并过滤查询。
                                                                        
                                                                        默认情况下，它返回（未过滤的）存储库（已经是IQueryable<TEntity>）。

                                                            ②、ApplyPaging用于对查询进行分页。
                                                                        
                                                                        如果你的TGetListInput已经实现了IPagedResultRequest，你不需要覆盖它，因为ABP会自动理解它并执行分页。

                                                            ③、ApplySorting用于对查询进行排序（按…排序）。

                                                                        如果你的TGetListInput已经实现了ISortedResultRequest，ABP会自动对查询进行排序。

                                                                        如果不是，如果你的实体实现了标准的IHasCreationTime接口，它会回退到尝试按创建时间排序的ApplyDefaultSorting。

                                                            ④、GetEntityByIdAsync用于通过id获取实体，默认调用Repository.GetAsync(id)。
   
                                                            ⑤、DeleteByIdAsync用于按id删除实体，默认调用Repository.DeleteAsync(id)。
                                                            

                                5、对象映射

                                         这些方法用于将实体转换为DTO，反之亦然。默认情况下，它们使用IObjectMapper。


                                                        ①、MapToGetOutputDtoAsync用于将实体映射到从GetAsync、CreateAsync和UpdateAsync方法返回的DTO

                                                                            或者，如果不需要执行任何异步操作，你可以覆盖MapToGetOutputDto。

                                                        ②、MapToGetListOutputDtosAsync用于将实体列表映射到从GetListAsync方法返回的DTO列表。

                                                                            它使用MapToGetListOutputDtoAsync映射列表中的每个实体。

                                                                            你可以根据自己的情况覆盖其中一个。

                                                                            或者，如果不需要执行任何异步操作，你可以覆盖MapToGetListOutputDto。

                                                        ③、MapToEntityAsync方法有两个重载：

                                                                            MapToEntityAsync(TCreateInput)用于从TCreateInput创建实体。

                                                                            MapToEntityAsync(TUpdateInput, TEntity)用于从TUpdateInput更新现有实体。
                                                                
                    */

        </script>
    </body>
</html>